package com.foreverwin.mes.production.hook;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.foreverwin.mes.common.enums.HandleEnum;
import com.foreverwin.mes.core.meext.ExtBusinessException;
import com.foreverwin.mes.core.utils.DateUtil;
import com.foreverwin.mes.core.utils.I18nUtil;
import com.foreverwin.mes.core.utils.SpringUtil;
import com.foreverwin.mes.core.utils.StringUtils;
import com.foreverwin.mes.meapi.model.*;
import com.foreverwin.mes.meapi.service.*;
import com.foreverwin.mes.production.constant.Constants;
import com.foreverwin.mes.production.model.MaterialConsumption;
import com.foreverwin.mes.production.model.ResourceInventory;
import com.foreverwin.mes.production.service.MaterialConsumptionService;
import com.foreverwin.mes.production.service.ResourceInventoryService;
import com.foreverwin.mes.production.support.LoadStrategyFactory;
import com.google.common.collect.Lists;
import com.sap.me.production.PostCompleteHookDTO;
import com.visiprise.frame.service.ext.ActivityInterface;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Optional;

/**
 * 物料耗用HOOK
 *
 * @author Ervin Chen
 * @date 2020/3/10 15:25
 */
public class MaterialConsumptionHook implements ActivityInterface<PostCompleteHookDTO> {

    //private final Logger logger = LoggerFactory.getLogger(MaterialConsumptionHook.class);

    private SfcBomService sfcBomService;
    private BomComponentService bomComponentService;
    private CustomFieldsService customFieldsService;
    private ResourceInventoryService resourceInventoryService;
    private InventoryAssyDataService inventoryAssyDataService;
    private InventoryService inventoryService;
    private SfcService sfcService;
    private MaterialConsumptionService materialConsumptionService;

    public void initService() {
        sfcBomService = SpringUtil.getBean(SfcBomService.class);
        bomComponentService = SpringUtil.getBean(BomComponentService.class);
        customFieldsService = SpringUtil.getBean(CustomFieldsService.class);
        resourceInventoryService = SpringUtil.getBean(ResourceInventoryService.class);
        inventoryAssyDataService = SpringUtil.getBean(InventoryAssyDataService.class);
        inventoryService = SpringUtil.getBean(InventoryService.class);
        sfcService = SpringUtil.getBean(SfcService.class);
        materialConsumptionService = SpringUtil.getBean(MaterialConsumptionService.class);
    }

    @Override
    public void execute(PostCompleteHookDTO dto) throws Exception {
        String site = dto.getSite();
        String userBo = dto.getUserBO().getValue();
        String sfcBo = dto.getSfcBO().getValue();
        String resourceBo = dto.getResourceBO().getValue();
        String resource = HandleEnum.RESOURCE.getPart(resourceBo, 1);
        BigDecimal qty = dto.getQty();

        initService();

        //查询工单
        Sfc sfcDo = sfcService.selectById(sfcBo);
        String shopOrderBo = sfcDo.getShopOrderBo();
        String shopOrder = HandleEnum.SHOP_ORDER.getPart(shopOrderBo, 1);

        //查询bom
        SfcBom queryEntity = new SfcBom();
        queryEntity.setSfcBo(sfcBo);
        List<SfcBom> sfcBoms = sfcBomService.selectList(queryEntity);
        if (sfcBoms.isEmpty()) {
            return;
        }

        //查询bom组件
        String bomBo = sfcBoms.get(0).getBomBo();
        List<BomComponent> bomComponents = bomComponentService.listByBomBo(bomBo);
        if (bomComponents.isEmpty()) {
            return;
        }

        //扣料逻辑
        for (BomComponent bomComponent : bomComponents) {
            String componentBo = bomComponent.getComponentGbo();
            Double unitQty = bomComponent.getQty();
            BigDecimal actualQty = qty.multiply(BigDecimal.valueOf(unitQty));
            String loadMode = resourceInventoryService.getItemLoadMode(componentBo);
            Boolean isInventoryExist = false;

            if (!StringUtils.isBlank(loadMode)) {  //设备上料
                //查询上料设备
                CustomFields feedResourceField = customFieldsService.getAttributeValue(resourceBo, Constants.FEED_RESOURCE);
                String[] feedResources = null;
                if (feedResourceField != null) {
                    feedResources = feedResourceField.getValue().split(",");
                } else {
                    feedResources = new String[]{resource};
                }
                List<ResourceInventory> resourceInventories = null;
                switch (LoadStrategyFactory.valueOf(loadMode)) {
                    case MSR01:
                        resourceInventories = getResourceInventoryList(componentBo, shopOrder, feedResources);
                        consumeResourceInventoryForMSR01(site, userBo, componentBo, actualQty, sfcDo, resourceInventories);
                        break;
                    case MSR02:
                        resourceInventories = getResourceInventoryList(componentBo, null, feedResources);
                        consumeResourceInventoryForMSR02(site, userBo, componentBo, actualQty, sfcDo, resourceInventories);
                        break;
                    case MSR03:
                        resourceInventories = getResourceInventoryList(componentBo, null, feedResources);
                        consumeResourceInventoryForMSR03(site, userBo, componentBo, actualQty, sfcDo, resourceInventories);
                        break;
                }
                isInventoryExist = true;
            } else {
                //查找设备耗用信息的存储位置信息，通过存储位置找到对应库存先进先出扣料
                //布勒通过MQ接口查询存储位置；科瑞没有存储位置信息按未提供存储位置处理；申克通过opc

                //查找设备预留库存扣料
                //查找库存预留设备
                CustomFields reserveResourceField = customFieldsService.getAttributeValue(resourceBo, Constants.INV_RESERVE_RESOURCE);
                String[] reserveResources = new String[]{};
                if (reserveResourceField != null) {
                    reserveResources = reserveResourceField.getValue().split(",");
                }
                if (reserveResources.length > 0) {
                    consumeResourceReserveInventory(site, userBo, componentBo, actualQty, sfcDo, reserveResources);
                    isInventoryExist = true;
                }
            }
            if (!isInventoryExist) {
                throw new ExtBusinessException("物料[" + HandleEnum.ITEM.getPart(componentBo, 1)+"]没有对应的库存扣料");
            }
        }
    }

    /**
     * 上料模式一扣料
     *
     * @param site
     * @param userBo
     * @param componentBo
     * @param actualQty
     * @param sfcDo
     * @param resourceInventories
     * @throws Exception
     */
    public void consumeResourceInventoryForMSR01(String site, String userBo, String componentBo, BigDecimal actualQty, Sfc sfcDo, List<ResourceInventory> resourceInventories) throws Exception {
        BigDecimal _actualQty = BigDecimal.valueOf(actualQty.doubleValue());
        String component = HandleEnum.ITEM.getPart(componentBo, 1);
        for (ResourceInventory resourceInventory : resourceInventories) {
            if (resourceInventory.getQtyOnHand() != null) {
                String inventoryId = resourceInventory.getInventoryId();
                String batchNumber = getBatchNumber(site, inventoryId);
                BigDecimal qtyOnHand = BigDecimal.valueOf(resourceInventory.getQtyOnHand());
                BigDecimal costQty = null;
                Date now = new Date();

                if (_actualQty.doubleValue() > qtyOnHand.doubleValue()) {
                    costQty = qtyOnHand;
                    _actualQty = _actualQty.subtract(qtyOnHand);
                    resourceInventoryService.deleteById(resourceInventory.getHandle());
                } else {
                    costQty = _actualQty;
                    qtyOnHand = qtyOnHand.subtract(_actualQty);
                    _actualQty = BigDecimal.ZERO;
                    resourceInventory.setQtyOnHand(qtyOnHand.doubleValue());
                    resourceInventory.setUpdateUserBo(userBo);
                    resourceInventory.setUpdatedDateTime(now);
                    resourceInventoryService.updateById(resourceInventory);
                }

                //保存物料耗用
                saveMaterialConsumption(site, userBo, sfcDo, componentBo, inventoryId, batchNumber, getStorageLocation(site, inventoryId), costQty);

                if (_actualQty.doubleValue() == 0) {
                    break;
                }
            }
        }
        if (_actualQty.doubleValue() > 0) {
            throw new ExtBusinessException(I18nUtil.getI18nText("production.materialConsumption.needQtyOverQtyOnHand", new Object[]{component, actualQty.doubleValue(), actualQty.subtract(_actualQty).doubleValue()}));
        }
    }

    /**
     * 上料模式二扣料
     *
     * @param site
     * @param userBo
     * @param componentBo
     * @param actualQty
     * @param sfcDo
     * @param resourceInventories
     * @throws Exception
     */
    public void consumeResourceInventoryForMSR02(String site, String userBo, String componentBo, BigDecimal actualQty, Sfc sfcDo, List<ResourceInventory> resourceInventories) throws Exception {
        consumeResourceInventoryForMSR01(site, userBo, componentBo, actualQty, sfcDo, resourceInventories);
    }

    /**
     * 上料模式三扣料
     *
     * @param site
     * @param userBo
     * @param componentBo
     * @param actualQty
     * @param sfcDo
     * @param resourceInventories
     * @throws Exception
     */
    public void consumeResourceInventoryForMSR03(String site, String userBo, String componentBo, BigDecimal actualQty, Sfc sfcDo, List<ResourceInventory> resourceInventories) throws Exception {
        BigDecimal _actualQty = BigDecimal.valueOf(actualQty.doubleValue());
        String component = HandleEnum.ITEM.getPart(componentBo, 1);
        for (ResourceInventory resourceInventory : resourceInventories) {
            //查询供应商批次可用库存
            String vendorLot = resourceInventory.getVendorLot();
            List<Inventory> inventories = getInventoryListByDataField(site, componentBo, Constants.VENDOR_LOT, vendorLot);
            if (inventories.isEmpty()) {
                throw new ExtBusinessException(I18nUtil.getI18nText("production.resourceInventory.inventoryWithBatchNumberItemNotExist", new Object[]{vendorLot, component}));
            }
            consumeInventory(site, userBo, componentBo, _actualQty, sfcDo, inventories);
        }
        if (_actualQty.doubleValue() > 0) {
            throw new ExtBusinessException(I18nUtil.getI18nText("production.materialConsumption.needQtyOverQtyOnHand", new Object[]{component, actualQty.doubleValue(), actualQty.subtract(_actualQty).doubleValue()}));
        }
    }

    /**
     * 设备预留库存扣料
     *
     * @param site
     * @param userBo
     * @param componentBo
     * @param actualQty
     * @param sfcDo
     * @param reserveResources
     * @throws ExtBusinessException
     */
    public void consumeResourceReserveInventory(String site, String userBo, String componentBo, BigDecimal actualQty, Sfc sfcDo, String[] reserveResources) throws ExtBusinessException {
        String component = HandleEnum.ITEM.getPart(componentBo, 1);
        //查询库存预留设备可用库存
        List<Inventory> inventories = getInventoryListByDataField(site, componentBo, Constants.FROM_RESOURCE, reserveResources);
        if (inventories.isEmpty()) {
            throw new ExtBusinessException(I18nUtil.getI18nText("production.materialConsumption.resourceReserveInventoryNotExist", new Object[]{String.join(",", reserveResources), HandleEnum.ITEM.getPart(componentBo, 1)}));
        }
        BigDecimal _actualQty = BigDecimal.valueOf(actualQty.doubleValue());
        consumeInventory(site, userBo, componentBo, _actualQty, sfcDo, inventories);
        if (_actualQty.doubleValue() > 0) {
            throw new ExtBusinessException(I18nUtil.getI18nText("production.materialConsumption.needQtyOverQtyOnHand", new Object[]{component, actualQty.doubleValue(), actualQty.subtract(_actualQty).doubleValue()}));
        }
    }

    /**
     * 扣除库存表数量
     *
     * @param site
     * @param userBo
     * @param componentBo
     * @param actualQty
     * @param sfcDo
     * @param inventories
     * @throws ExtBusinessException
     */
    public void consumeInventory(String site, String userBo, String componentBo, BigDecimal actualQty, Sfc sfcDo, List<Inventory> inventories) throws ExtBusinessException {
        for (Inventory inventory : inventories) {
            String inventoryId = inventory.getInventoryId();
            String batchNumber = getBatchNumber(site, inventoryId);
            BigDecimal qtyOnHand = BigDecimal.valueOf(inventory.getQtyOnHand());
            String storageLocationBo = inventory.getStorageLocationBo();
            String storageLocation = storageLocationBo != null ? HandleEnum.STORAGE_LOCATION.getPart(storageLocationBo, 1) : null;
            BigDecimal costQty = null;

            if (actualQty.doubleValue() > qtyOnHand.doubleValue()) {
                costQty = qtyOnHand;
                actualQty = actualQty.subtract(qtyOnHand);
                inventory.setQtyOnHand(0d);
                inventoryService.updateById(inventory);
            } else {
                costQty = actualQty;
                qtyOnHand = qtyOnHand.subtract(actualQty);
                actualQty = BigDecimal.ZERO;
                inventory.setQtyOnHand(qtyOnHand.doubleValue());
                inventoryService.updateById(inventory);
            }

            //保存物料耗用
            saveMaterialConsumption(site, userBo, sfcDo, componentBo, inventoryId, batchNumber, storageLocation, costQty);

            if (actualQty.doubleValue() == 0) {
                break;
            }
        }

    }

    /**
     * 保存物料消耗
     *
     * @param site
     * @param userBo
     * @param sfcDo
     * @param componentBo
     * @param inventoryId
     * @param batchNumber
     * @param storageLocation
     * @param costQty
     */
    public void saveMaterialConsumption(String site, String userBo, Sfc sfcDo, String componentBo, String inventoryId, String batchNumber, String storageLocation, BigDecimal costQty) {
        String sfc = sfcDo.getSfc();
        String item = HandleEnum.ITEM.getPart(sfcDo.getItemBo(), 1);
        String shopOrderBo = sfcDo.getShopOrderBo();
        String shopOrder = HandleEnum.SHOP_ORDER.getPart(shopOrderBo, 1);
        String component = HandleEnum.ITEM.getPart(componentBo, 1);

        Date now = new Date();
        MaterialConsumption materialConsumption = new MaterialConsumption();
        materialConsumption.setHandle(HandleEnum.MATERIAL_CONSUMPTION.getHandle(site, sfc, DateUtil.format(now)));
        materialConsumption.setSite(site);
        materialConsumption.setShopOrder(shopOrder);
        materialConsumption.setSfc(sfc);
        materialConsumption.setItem(item);
        materialConsumption.setComponent(component);
        materialConsumption.setInventoryId(inventoryId);
        materialConsumption.setBatchNumber(batchNumber);
        materialConsumption.setStorageLocation(storageLocation);
        materialConsumption.setQty(costQty.doubleValue());
        materialConsumption.setSendErp("N");
        materialConsumption.setCreateUserBo(userBo);
        materialConsumption.setCreatedDateTime(now);
        materialConsumptionService.insert(materialConsumption);
    }

    public String getStorageLocation(String site, String inventoryId) {
        Inventory inventory = inventoryService.selectById(HandleEnum.INVENTORY.getHandle(site, inventoryId));
        String storageLocationBo = inventory.getStorageLocationBo();
        return storageLocationBo != null ? HandleEnum.STORAGE_LOCATION.getPart(storageLocationBo, 1) : null;
    }

    public List<ResourceInventory> getResourceInventoryList(String componentBo, String shopOrder, String[] feedResources) throws ExtBusinessException {
        String component = HandleEnum.ITEM.getPart(componentBo, 1);
        EntityWrapper<ResourceInventory> entityWrapper = new EntityWrapper<>();
        entityWrapper.in(ResourceInventory.RESRCE, feedResources).eq(ResourceInventory.ITEM_BO, componentBo).orderAsc(Arrays.asList(ResourceInventory.CREATED_DATE_TIME));
        if (!StringUtils.isBlank(shopOrder)) {
            entityWrapper.eq(ResourceInventory.SHOP_ORDER, shopOrder);
        }
        List<ResourceInventory> resourceInventories = resourceInventoryService.selectList(entityWrapper);
        if (resourceInventories == null || resourceInventories.isEmpty()) {
            throw new ExtBusinessException(I18nUtil.getI18nText("production.materialConsumption.resourceNotLoadItem", new String[]{component, String.join(",", feedResources)}));
        }
        return resourceInventories;
    }

    public List<Inventory> getInventoryListByDataField(String site, String componentBo, String dataField, String... dataAttr) {
        EntityWrapper<Inventory> inventoryWrapper = new EntityWrapper<>();
        inventoryWrapper.eq("site", site)
                .gt("qty_on_hand", 0)
                .eq("item_bo", componentBo)
                .eq("status_bo", HandleEnum.STATUS.getHandle(site, Constants.INVENTORY_AVAILABLE_STATUS))
                .orderAsc(Arrays.asList("receive_date_time"));
        List<Inventory> inventories = inventoryService.listByAssyData(inventoryWrapper, dataField, Lists.newArrayList(dataAttr));
        return inventories;
    }

    public String getBatchNumber(String site, String inventoryId) {
        String batchNumber = null;
        String inventoryBo = HandleEnum.INVENTORY.getHandle(site, inventoryId);
        InventoryAssyData inventoryAssyDataQuerier = new InventoryAssyData();
        inventoryAssyDataQuerier.setInventoryBo(inventoryBo);
        inventoryAssyDataQuerier.setDataField(Constants.BATCH_NUMBER);
        Optional<InventoryAssyData> inventoryAssyDataOpt = inventoryAssyDataService.selectList(inventoryAssyDataQuerier).stream().findFirst();
        if (inventoryAssyDataOpt.isPresent()) {
            batchNumber = inventoryAssyDataOpt.get().getDataAttr();
        }
        return batchNumber;
    }

}
